#cmake file for project thmar
#author:guyadong
#created:2024/08/11
##! 用于velrelcm.sh 的 变量搜索正则表达式, 不要轻易修改
##! @PRJNAME_REG@          (project\s*\(\s*)([[:alnum:]-]+)(\s+.*\))
##! @VERSION_REG@          (project\s*\(\s*[[:alnum:]-]+\s+VERSION\s+)(\S*)(.*\))
##! @DESC_REG@             (set\s*\(\s*THMAR_CPP_VER_DESC\s+)(\S*)?(\s*\))
##! @SNAPSHOT_REG@         (project\s*\(\s*[[:alnum:]-]+\s+VERSION\s+[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+)(\.1)?(.*\))
##! @BEANCH_REG@           (set\s*\(\s*THMAR_CPP_SCM_BRANCH\s+)(\S*)?(\s*\))
##! @COMMIT_REG@           (set\s*\(\s*THMAR_CPP_SCM_COMMIT\s+)(\S*)?(\s*\))
##! 用于头文件(header)中搜索版本定义的正则表达
##! @H_VERSION_MAJOR_REG@  (#define\s+THMAR_CPP_VERSION_MAJOR\s+)(\S+)(\s*)
##! @H_VERSION_MINOR_REG@  (#define\s+THMAR_CPP_VERSION_MINOR\s+)(\S+)(\s*)
##! @H_VERSION_PATCH_REG@  (#define\s+THMAR_CPP_VERSION_PATCH\s+)(\S+)(\s*)
##! @H_VERSION_SNAPSHOT_REG@  (#define\s+THMAR_CPP_VERSION_SNAPSHOT)(\s+\S+)(\s*)
cmake_minimum_required( VERSION 3.8 )
if(POLICY CMP0048)
  cmake_policy(SET CMP0048 NEW)
endif()
# 3.0以上版本才允许使用VERSION option
project(thmar VERSION 0.0.0.1 LANGUAGES C CXX)
#判断编译类型和版本是否满足编译要求
list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_11 _cxx11_enable)
if(NOT _cxx11_enable)
	message(FATAL_ERROR "Compiler not supported C++ 11 standard")
endif()
unset(_cxx11_enable)
# 要求编译器支持C11
set(CMAKE_C_STANDARD 11)
SET(CMAKE_C_STANDARD_REQUIRED ON)
# 要求编译器支持C++11
set(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_STANDARD_REQUIRED ON)
# 在支持层次工程组织的IDE(MSVC)中使用"FOLDER"属性定义的文件夹名称来组织项目
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# 定义库名
set(THMAR_LIBNAME thmar)

option(WITH_PAHO "Enable paho mqtt" OFF)
option(WITH_IF_UTILITS_C "using if_utilits_c in common_source_cpp" OFF)
option(BUILD_TEST "Build test" ON)
##############设置目标文件生成位置#####################
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
# define LIBRARY_INSTALL_DIR INCLUDE_INSTALL_DIR CONFIG_INSTALL_DIR
set (LIBRARY_INSTALL_DIR lib)
set (INCLUDE_INSTALL_DIR include)
if(WIN32 AND NOT CYGWIN)
  set (CONFIG_INSTALL_DIR  cmake)
else()
  set (CONFIG_INSTALL_DIR  ${LIBRARY_INSTALL_DIR}/cmake/${INTFACE_NAME})
endif()

# 定义所有的源文件列表
# 使用file(GLOB)命令收集指定目录下的文件，不包括子目录
file(GLOB _SRC_SOURCES ${CMAKE_SOURCE_DIR}/src/*.c ${CMAKE_SOURCE_DIR}/src/*.cpp)

# 创建 library target
add_library(${THMAR_LIBNAME} ${_SRC_SOURCES} )

## 编译选项设置 
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
	MESSAGE(STATUS "GNU CXX compiler flags: ${CMAKE_CXX_FLAGS}")
endif()

target_compile_options(${THMAR_LIBNAME} PRIVATE $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<CONFIG:Debug>>:/Z7> 
	# 使用sanitize进行内存泄露检查
	$<$<AND:$<CXX_COMPILER_ID:GNU>,$<PLATFORM_ID:Linux>,$<BOOL:BUILD_TEST>>:-fsanitize=address> 
	)
target_compile_definitions(${THMAR_LIBNAME} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/wd4819 -D_CRT_SECURE_NO_WARNINGS>)
# 使用sanitize进行内存泄露检查
target_link_options(${THMAR_LIBNAME} PUBLIC $<$<AND:$<CXX_COMPILER_ID:GNU>,$<PLATFORM_ID:Linux>,$<BOOL:BUILD_TEST>>:-fsanitize=address -static-libasan>)
message(STATUS "target ${THMAR_LIBNAME}")
target_include_directories (${THMAR_LIBNAME} INTERFACE "$<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>" 
 	PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/mqtt/include>" 
	"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>")
set(_public_headers)
list(APPEND _public_headers 
	${CMAKE_CURRENT_LIST_DIR}/src/MqInvoker.h 
	${CMAKE_CURRENT_LIST_DIR}/src/rwlock.h 
	${CMAKE_CURRENT_LIST_DIR}/src/rwlock_pthread.h 
	${CMAKE_CURRENT_LIST_DIR}/src/rwlock_freertos.h 
	${CMAKE_CURRENT_LIST_DIR}/src/local_error.h 
	${CMAKE_CURRENT_LIST_DIR}/mqtt/include/thmar_types.h 
	${CMAKE_CURRENT_LIST_DIR}/mqtt/include/thmar_config.h 
	${CMAKE_CURRENT_LIST_DIR}/mqtt/include/ThmarMqttClient.h 
	)

# 引入CheckSymbolExists模块
include(CheckSymbolExists)

# 检查是否存在malloc函数
check_symbol_exists(malloc "stdlib.h" HAS_MALLOC)

if(HAS_MALLOC)
    add_definitions(-DHAS_MALLOC)
endif()

# 检查是否存在free函数
check_symbol_exists(free "stdlib.h" HAS_FREE)

if(HAS_FREE)
    add_definitions(-DHAS_FREE)
endif()

include(CheckIncludeFile)
CHECK_INCLUDE_FILE(uthash.h HAS_UTHASH_H)
if(HAS_UTHASH_H)
# 有些嵌入式平台SDK已经提供了 uthash 支持,就不需要 find_package
# DO NOTHING
else()
	message(STATUS "NOT FOUND uthash.h,try to find uthash package")
	## 查找 uthash 库
	find_package(uthash REQUIRED)
	target_include_directories(${THMAR_LIBNAME} PUBLIC "$<BUILD_INTERFACE:${uthash_INCLUDE_DIRS}>")
endif(HAS_UTHASH_H)
if(NOT CJSON_HEADER)
	CHECK_INCLUDE_FILE(cJSON.h HAS_CJSON_H)
	if(HAS_CJSON_H)
	# 有些嵌入式平台SDK已经提供了 cJSON 支持,就不需要 find_package
	# DO NOTHING
	else()
		message(STATUS "NOT FOUND cJSON.h,try to find cJSON package")
		## 如果没有找到 cJSON.h 则查找 cJSON 库
		find_package(cJSON REQUIRED)
		if(TARGET cjson)
			# cJSON头文件正常引用方式为#include <cjson/cJSON.h>
			# 但esp32平台下cJSOn.h搜索路径不需要加cjson前缀,需要以#include <cJSON.h>方式引用
			# 为将就esp32平台,在cjson-static target中将cjson加入搜索路径
			target_include_directories(cjson INTERFACE "${cJSON_INCLUDE_DIR}/cjson")
			target_link_libraries(${THMAR_LIBNAME} PUBLIC cjson::cjson)
		else()
			message(FATAL_ERROR "NOT FOUND import target 'cjson'")
		endif()
	endif()
endif()

CHECK_INCLUDE_FILE(sds.h HAS_SDS_H)
if(HAS_SDS_H)
# 有些嵌入式平台SDK已经提供了 sds 支持,就不需要使用 sds 文件夹的代码 
# DO NOTHING
else()
	message(STATUS "NOT FOUND sds.h,use sds folder")
	## 如果没有找到 sds.h
	# 则使用 sds 库文件夹
	aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/sds _sds_SOURCES)
	target_sources(${THMAR_LIBNAME} PRIVATE ${_sds_SOURCES})
	target_include_directories (${THMAR_LIBNAME} PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/sds>")
	list(APPEND _public_headers ${CMAKE_CURRENT_LIST_DIR}/sds/sds.h ${CMAKE_CURRENT_LIST_DIR}/sds/sdsalloc.h)
endif()


if(WITH_PAHO)
	find_package(eclipse-paho-mqtt-c REQUIRED)
	target_link_libraries(${THMAR_LIBNAME} PUBLIC eclipse-paho-mqtt-c::paho-mqtt3c-static)
	set(WITH_IF_UTILITS_C ON)
	# 定义paho mqtt 实现的所有源文件列表
	aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/mqtt/paho _paho_SOURCES)
	message(STATUS "paho mqtt sources: ${_paho_SOURCES}")
	target_sources(${THMAR_LIBNAME} PRIVATE ${_paho_SOURCES})
	target_include_directories (${THMAR_LIBNAME} PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/mqtt/paho>")
	list(APPEND _public_headers ${CMAKE_CURRENT_LIST_DIR}/mqtt/paho/PahoMqttClient.h)
endif()
	
if(TARGET_DEVICE STREQUAL "esp32")
	# 定义esp-mqtt 实现的所有源文件列表
	aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/mqtt/espmqtt _esp_mqtt_SOURCES)
	message(STATUS "esp-mqtt sources: ${_esp_mqtt_SOURCES}")
	target_sources(${THMAR_LIBNAME} PRIVATE ${_esp_mqtt_SOURCES})
	list(APPEND _public_headers ${CMAKE_CURRENT_LIST_DIR}/mqtt/espmqtt/EspMqttClient.h)
endif()
find_package(common_source_cpp REQUIRED)
# 获取common_source_cpp的文件夹
get_target_property(_common_source_includes common_source_cpp::common_source_cpp INTERFACE_INCLUDE_DIRECTORIES)
# 定义 common_source_cpp 的 include文件夹位置
# 不知道为什么 find_file(var if_utilits_c.c ${_common_source_includes}) 只能返回 if_utilits_c.c 文件所在的路径，
# 所以只能用 find_path 找到路径
find_path(_common_source_include if_utilits_c.c ${_common_source_includes})
# 增加 debug_printf.c 文件到源文件列表
target_sources(${THMAR_LIBNAME} PRIVATE ${_common_source_include}/debug_printf.c)
list(APPEND _public_headers ${_common_source_include}/debug_printf.h)
target_include_directories(${THMAR_LIBNAME} PUBLIC "$<BUILD_INTERFACE:${_common_source_include}>")

if(WITH_IF_UTILITS_C)
	# target_link_libraries(${THMAR_LIBNAME} PRIVATE common_source_cpp::common_source_cpp)
	target_sources(${THMAR_LIBNAME} PRIVATE ${_common_source_include}/if_utilits_c.c)
	list(APPEND _public_headers ${_common_source_include}/if_utilits_c.h)
	target_compile_definitions(${THMAR_LIBNAME} PRIVATE WITH_IF_UTILITS_C)
	target_link_libraries(${THMAR_LIBNAME} INTERFACE $<$<PLATFORM_ID:Windows>:iphlpapi>)
endif()
target_link_libraries(${THMAR_LIBNAME} INTERFACE $<$<PLATFORM_ID:Android>:log> $<$<PLATFORM_ID:Linux>:-pthread -ldl>)

set_target_properties (${THMAR_LIBNAME}  PROPERTIES 
	PUBLIC_HEADER "${_public_headers}"
	FOLDER "thmar"
	)
add_library(${PROJECT_NAME}::${THMAR_LIBNAME} ALIAS ${THMAR_LIBNAME})

# 定义测试用例
if(BUILD_TEST)
	enable_testing()
	# add_subdirectory(test)
	add_subdirectory(unittest)
endif()

include (CMakePackageConfigHelpers)
if(HAS_CJSON_H)
	set (thmar_DEPENDENCY "")
else()
	set (thmar_DEPENDENCY "find_dependency(cJSON REQUIRED)")
endif()
configure_package_config_file (${PROJECT_SOURCE_DIR}/cmake/config.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake
  INSTALL_DESTINATION ${CONFIG_INSTALL_DIR}
  NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file (${PROJECT_NAME}-config-version.cmake VERSION
  ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
# 定义安装规则
install(TARGETS ${THMAR_LIBNAME} EXPORT ${PROJECT_NAME}-targets
RUNTIME DESTINATION ${LIBRARY_INSTALL_DIR}
LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR}
ARCHIVE DESTINATION ${LIBRARY_INSTALL_DIR}
INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR}/${THMAR_LIBNAME}
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/${THMAR_LIBNAME})

install (FILES
  ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
  DESTINATION ${CONFIG_INSTALL_DIR}
  )

# 定义导出规则
install(EXPORT ${PROJECT_NAME}-targets
		NAMESPACE ${PROJECT_NAME}::
		DESTINATION ${CONFIG_INSTALL_DIR})
